#include "ohos_player.h"
#include <multimedia/player_framework/native_avdemuxer.h>
#include <multimedia/player_framework/native_avsource.h>
#include <multimedia/player_framework/native_avcodec_base.h>
#include <multimedia/player_framework/native_avformat.h>
// fatal error: 'multimedia/player_framework/native_avbuffer.h' file not found
#include <multimedia/player_framework/native_avbuffer.h>

#include <multimedia/player_framework/native_avcodec_videodecoder.h>
#include <multimedia/player_framework/native_avcapability.h>
#include <multimedia/player_framework/native_avcodec_base.h>
#include <multimedia/player_framework/native_avformat.h>
#include <multimedia/player_framework/native_avbuffer.h>

#include <fstream>
#include <iostream>

// 解码异常回调OH_AVCodecOnError实现
static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData) {
    (void)codec;
    (void)errorCode;
    (void)userData;
}

// 解码数据流变化回调OH_AVCodecOnStreamChanged实现
static void OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData) {
    (void)codec;
    (void)format;
    (void)userData;
}

// 解码输入回调OH_AVCodecOnNeedInputBuffer实现
static void OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
    // 输入帧buffer对应的index，送入InIndexQueue队列
    // 输入帧的数据buffer送入InBufferQueue队列
    // 数据处理，请参考:
    // - 写入解码码流

    ///////////////////////////
    // 写入解码码流。

    // 从解封装过来的数据不需要设置这些参数
    // 配置帧数据的输入尺寸、偏移量、时间戳等字段信息
    // OH_AVCodecBufferAttr info;
    // info.size = size;
    // info.offset = offset;
    // info.pts = pts;
    // info.flags = flags;
    // // info信息写入buffer
    // int32_t ret = OH_AVBuffer_SetBufferAttr(buffer, &info);
    // if (ret != AV_ERR_OK) {
    //     // 异常处理
    // }

    OHPlayer *player = reinterpret_cast<OHPlayer *>(userData);
    if (!player) {
        // 异常处理
        return;
    }

    // 解封装填充buffer
    player->ReadSampleBuffer(buffer);

    // 送入解码输入队列进行解码，index为对应队列下标
    int32_t ret = OH_VideoDecoder_PushInputBuffer(codec, index);
    if (ret != AV_ERR_OK) {
        // 异常处理
    }
}

// 解码输出回调OH_AVCodecOnNewOutputBuffer实现
static void OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
    // 完成帧buffer对应的index，送入outIndexQueue队列
    // 完成帧的数据buffer送入outBufferQueue队列
    // 数据处理，请参考:
    // - 显示并释放解码帧

    ///////////////////////////
    // 显示并释放解码帧

    OHPlayer *player = reinterpret_cast<OHPlayer *>(userData);
    if (!player) {
        // 异常处理
        return;
    }

    // 获取解码后信息
    OH_AVCodecBufferAttr info;
    int32_t ret = OH_AVBuffer_GetBufferAttr(buffer, &info);
    if (ret != AV_ERR_OK) {
        // 异常处理
    }
    if (player->IsRender()) {
        // 显示并释放已完成处理的信息，index为对应buffer队列下标
        ret = OH_VideoDecoder_RenderOutputBuffer(codec, index);
    } else {
        // 释放已完成处理的信息
        ret = OH_VideoDecoder_FreeOutputBuffer(codec, index);
    }
    if (ret != AV_ERR_OK) {
        // 异常处理
    }
}

void OHPlayer::init(const std::string &url) {
    ohSource = OH_AVSource_CreateWithURI(const_cast<char *>(url.c_str()));

    ///////////////////////////
    // 为资源对象创建对应的解封装器
    ohDemuxer = OH_AVDemuxer_CreateWithSource(ohSource);
    if (ohDemuxer == nullptr) {
        printf("create demuxer failed");
        return;
    }

    ///////////////////////////
    // 从文件 source 信息获取文件轨道数
    OH_AVFormat *sourceFormat = OH_AVSource_GetSourceFormat(ohSource);
    if (sourceFormat == nullptr) {
        printf("get source format failed");
        return;
    }
    int32_t trackCount = 0;
    OH_AVFormat_GetIntValue(sourceFormat, OH_MD_KEY_TRACK_COUNT, &trackCount);
    OH_AVFormat_Destroy(sourceFormat);

    ///////////////////////////
    // 获取轨道index及信息
    int32_t trackType;
    for (uint32_t index = 0; index < (static_cast<uint32_t>(trackCount)); index++) {
        // 获取轨道信息
        OH_AVFormat *trackFormat = OH_AVSource_GetTrackFormat(ohSource, index);
        if (trackFormat == nullptr) {
            printf("get track format failed");
            return;
        }
        OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_TRACK_TYPE, &trackType);
        static_cast<OH_MediaType>(trackType) == OH_MediaType::MEDIA_TYPE_AUD ? audioTrackIndex = index
                                                                             : videoTrackIndex = index;
        // 获取视频轨宽高
        if (trackType == OH_MediaType::MEDIA_TYPE_VID) {
            OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_WIDTH, &frameWidth);
            OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_HEIGHT, &frameHeight);
        }
        OH_AVFormat_Destroy(trackFormat);
    }

    ///////////////////////////
    // 添加解封装轨道
    if (OH_AVDemuxer_SelectTrackByID(ohDemuxer, audioTrackIndex) != AV_ERR_OK) {
        printf("select audio track failed: %d", audioTrackIndex);
        return;
    }
    if (OH_AVDemuxer_SelectTrackByID(ohDemuxer, videoTrackIndex) != AV_ERR_OK) {
        printf("select video track failed: %d", videoTrackIndex);
        return;
    }
    // 取消选择轨道(可选)
    // OH_AVDemuxer_UnselectTrackByID(demuxer, audioTrackIndex);
}

void OHPlayer::initCoder(void *nativeWindow) {
    ///////////////////////////
    // 创建解码器实例对象。
    // 通过codecname创建解码器，应用有特殊需求，比如选择支持某种分辨率规格的解码器，可先查询capability，再根据codec
    // name创建解码器。
    OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, false);
    const char *name = OH_AVCapability_GetName(capability);
    ohCodec = OH_VideoDecoder_CreateByName(name);

    ///////////////////////////
    // 调用OH_VideoDecoder_RegisterCallback()设置回调函数。
    // 注册回调函数指针集合OH_AVCodecCallback，包括：
    //    解码器运行错误；
    //    码流信息变化，如码流宽、高变化；
    //    运行过程中需要新的输入数据，即解码器已准备好，可以输入数据；
    //    运行过程中产生了新的输出数据，即解码完成。(注：Surface模式buffer参数为空)

    // 配置异步回调，调用 OH_VideoDecoder_RegisterCallback 接口
    OH_AVCodecCallback cb = {&OnError, &OnStreamChanged, &OnNeedInputBuffer, &OnNewOutputBuffer};
    // 配置异步回调
    int32_t ret = OH_VideoDecoder_RegisterCallback(ohCodec, cb, this);


    ///////////////////////////
    // 调用OH_VideoDecoder_Configure()配置解码器。
    // DEFAULT_WIDTH：320像素宽度；
    // DEFAULT_HEIGHT：240像素高度；
    // DEFAULT_PIXELFORMAT： 颜色格式，因为示例需要保存的YUV文件颜色格式是NV12，所以设置为 AV_PIXEL_FORMAT_NV12。

    // 配置视频帧宽度（必须）
    constexpr uint32_t DEFAULT_WIDTH = 320;
    // 配置视频帧高度（必须）
    constexpr uint32_t DEFAULT_HEIGHT = 240;
    // 配置视频颜色格式（必须）
    constexpr OH_AVPixelFormat DEFAULT_PIXELFORMAT = AV_PIXEL_FORMAT_NV12;

    OH_AVFormat *format = OH_AVFormat_Create();
    // 写入format
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, DEFAULT_WIDTH);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, DEFAULT_HEIGHT);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, DEFAULT_PIXELFORMAT);
    // 配置解码器
    ret = OH_VideoDecoder_Configure(ohCodec, format);
    if (ret != AV_ERR_OK) {
        // 异常处理
    }
    OH_AVFormat_Destroy(format);

    ///////////////////////////
    // 设置Surface。本例中的nativeWindow，需要从XComponent组件获取

    // 配置送显窗口参数
    ret = OH_VideoDecoder_SetSurface(ohCodec, static_cast<OHNativeWindow *>(nativeWindow)); // 从 XComponent 获取 window

    ///////////////////////////
    // 动态配置解码器surface参数。

    format = OH_AVFormat_Create();
    // 配置显示旋转角度
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_ROTATION, 90);
    // 配置视频与显示屏匹配模式（缩放与显示窗口适配，裁剪与显示窗口适配）
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_SCALING_MODE, SCALING_MODE_SCALE_CROP);
    ret = OH_VideoDecoder_SetParameter(ohCodec, format);
    OH_AVFormat_Destroy(format);

    ///////////////////////////
    // 调用OH_VideoDecoder_Prepare()解码器就绪。
    ret = OH_VideoDecoder_Prepare(ohCodec);
    if (ret != AV_ERR_OK) {
        // 异常处理
    }

    ///////////////////////////
    // 启动解码器
    // std::string_view inputFilePath = "/*yourpath*.h264";
    // std::unique_ptr<std::ifstream> inputFile = std::make_unique<std::ifstream>();
    // inputFile->open(inputFilePath.data(), std::ios::in | std::ios::binary);
    // 启动解码器，开始解码
    ret = OH_VideoDecoder_Start(ohCodec);
    if (ret != AV_ERR_OK) {
        // 异常处理
    }

    ///////////////////////////
    // 刷新解码器

    // 刷新解码器videoDec
    ret = OH_VideoDecoder_Flush(ohCodec);
    if (ret != AV_ERR_OK) {
        // 异常处理
    }
    // 重新开始解码
    ret = OH_VideoDecoder_Start(ohCodec);
    if (ret != AV_ERR_OK) {
        // 异常处理
    }
}

OHPlayer::~OHPlayer() {
    videoIsEnd = false;
    audioIsEnd = false;
    audioTrackIndex = 0;
    videoTrackIndex = 0;
    frameWidth = 0;
    frameHeight = 0;

    ///////////////////////////
    //    销毁解封装实例。
    // 需要用户调用 OH_AVSource_Destroy 接口成功后，手动将对象置为 NULL，对同一对象重复调用 OH_AVSource_Destroy
    // 会导致程序错误
    if (OH_AVSource_Destroy(ohSource) != AV_ERR_OK) {
        printf("destroy source pointer error");
    }
    ohSource = NULL;
    // 需要用户调用 OH_AVDemuxer_Destroy 接口成功后，手动将对象置为 NULL，对同一对象重复调用 OH_AVDemuxer_Destroy
    // 会导致程序错误
    if (OH_AVDemuxer_Destroy(ohDemuxer) != AV_ERR_OK) {
        printf("destroy demuxer pointer error");
    }
    ohDemuxer = NULL;
    // close(fd);


    ///////////////////////////
    // 停止解码器

    // 终止解码器videoDec
    int32_t ret = OH_VideoDecoder_Stop(ohCodec);
    if (ret != AV_ERR_OK) {
        // 异常处理
    }

    ///////////////////////////
    // 销毁解码器实例，释放资源

    // 调用OH_VideoDecoder_Destroy，注销解码器
    ret = OH_VideoDecoder_Destroy(ohCodec);
    if (ret != AV_ERR_OK) {
        // 异常处理
    }
}

void OHPlayer::ReadSampleBuffer(OH_AVBuffer *buffer) {

    buffer = OH_AVBuffer_Create(frameWidth * frameHeight * 3 >> 1);
    if (buffer == nullptr) {
        printf("build buffer failed");
        return;
    }
    OH_AVCodecBufferAttr info;

    int32_t ret;
    if (!audioIsEnd || !videoIsEnd) {
        // 在调用 OH_AVDemuxer_ReadSampleBuffer 接口获取数据前，需要先调用 OH_AVDemuxer_SelectTrackByID
        // 选中需要获取数据的轨道 获取音频帧数据
        if (!audioIsEnd) {
            ret = OH_AVDemuxer_ReadSampleBuffer(ohDemuxer, audioTrackIndex, buffer);
            if (ret == AV_ERR_OK) {
                // 可通过 buffer 获取并处理音频帧数据
                OH_AVBuffer_GetBufferAttr(buffer, &info);
                printf("audio info.size: %d\n", info.size);
                if (info.flags == OH_AVCodecBufferFlags::AVCODEC_BUFFER_FLAGS_EOS) {
                    audioIsEnd = true;
                }
            }
        }
        if (!videoIsEnd) {
            ret = OH_AVDemuxer_ReadSampleBuffer(ohDemuxer, videoTrackIndex, buffer);
            if (ret == AV_ERR_OK) {
                // 可通过 buffer 获取并处理视频帧数据
                OH_AVBuffer_GetBufferAttr(buffer, &info);
                printf("video info.size: %d\n", info.size);
                if (info.flags == OH_AVCodecBufferFlags::AVCODEC_BUFFER_FLAGS_EOS) {
                    videoIsEnd = true;
                }
            }
        }
    }
    // OH_AVBuffer_Destroy(buffer);
}

bool OHPlayer::IsRender() const { return isRender; }